package com.andy.myFramework.ioc.impl;



import com.andy.myFramework.ioc.BeanDefinition;
import com.andy.myFramework.ioc.BeanFactory;
import com.andy.myFramework.ioc.annotation.Autowire;
import com.andy.myFramework.ioc.annotation.Bean;
import com.andy.myFramework.ioc.annotation.Component;
import com.andy.myFramework.ioc.annotation.Configuration;
import com.andy.myFramework.util.AnnotationUtils;
import com.andy.myFramework.util.ScannerUtil;
import com.andy.myFramework.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class DefaultBeanFactory implements BeanFactory {
    //类扫描工具
    private ScannerUtil scannerUtil = new ScannerUtil();
    //维护了name与bean定义的map
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
    //所有name的列表
    private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
    //当使用type获取bean的时候，做的一个缓存，key是类，val是容器管理的bean的name数组
    private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<>(64);
    //最终成型的单例池
    private final Map<String,Object>  singletons = new ConcurrentHashMap<String, Object>();

    /**
     * 初始化bean工厂，加载bean到单例池
     * @throws Exception
     */
    public void initBeanFactory(String scanClassPath) throws Exception {

        //1-初始化所有bean定义
        initBeanDefinitions();

        //2-初始化单例池
        initSingletons();

    }

    /**
     * 初始化单例池
     * @throws Exception
     */
    private void initBeanDefinitions() throws Exception {
        initSimpleBeanDefinitions();
        initFactoryBeanDefinitions();
    }

    /**
     * 初始化普通beanDefinition
     * @throws Exception
     */
    private void initSimpleBeanDefinitions() throws Exception {
        //1-扫描得到所有拥有component标签的类
        Set<Class<?>> clazzes = scannerUtil.getAnnotationClasses("com.andy", Component.class);

        //2-将扫描到的类信息封装成beanDefinition并保存
        for (Class<?> clazz : clazzes) {
            Component component = AnnotationUtils.getAnnotation(clazz,Component.class);
            //获取bean的name，如果注解定义了，则使用定义的；如果没有定义默认类名首字母小写
            String name = component.name();
            if(StringUtils.isBlank(name)){
                name = StringUtils.toLowerCaseFirstOne(clazz.getSimpleName());
            }
            //如果已经存在name了，那么直接抛错
            if(beanDefinitionNames.contains(name)) throw new RuntimeException("bean name存在重复！name="+name);
            //创建beanDefinition
            DefaultBeanDefinition beanDefinition = new DefaultBeanDefinition(name, clazz);
            //管理beanDefinition
            beanDefinitionMap.put(name,beanDefinition);
            beanDefinitionNames.add(name);
        }
    }

    /**
     * 初始化工厂beanDefinition
     * @throws Exception
     */
    private void initFactoryBeanDefinitions() throws Exception {
        //1-扫描得到所有拥有configuration标签的类
        Set<Class<?>> clazzes = scannerUtil.getAnnotationClasses("com.andy", Configuration.class);

        for (Class<?> clazz : clazzes) {
            //2-先把有configuration的类本身放入容器管理
            String factoryBeanName = StringUtils.toLowerCaseFirstOne(clazz.getSimpleName());
            DefaultBeanDefinition factoryBeanDefinition = new DefaultBeanDefinition(factoryBeanName, clazz);
            beanDefinitionMap.put(factoryBeanName,factoryBeanDefinition);
            beanDefinitionNames.add(factoryBeanName);

            //3-获取@Bean修饰的方法，把需要生产的bean的定义放入容器管理
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                Bean beanAnnotation = method.getAnnotation(Bean.class);
                if(beanAnnotation==null)continue;
                //获取方法出参作为bean的类型，并获得name
                Class<?> returnType = method.getReturnType();
                String name = StringUtils.toLowerCaseFirstOne(returnType.getSimpleName());
                //创建bean定义
                DefaultBeanDefinition beanDefinition = new DefaultBeanDefinition(name, returnType,
                        true, method, factoryBeanName);
                beanDefinitionMap.put(name,beanDefinition);
                beanDefinitionNames.add(name);
            }
        }
    }

    /**
     * 初始化单例池
     * @throws Exception
     */
    private void initSingletons() throws Exception{
        //循环beanDefinitionNames
        for (String name : beanDefinitionNames) {
            getBean(name);
        }
    }

    /**
     * name获取bean
     * @param name
     * @return
     * @throws Exception
     */
    @Override
    public Object getBean(String name) throws Exception{
        //检查容器是否管理这个类型的bean,不管理则抛出异常
        checkIsManagedBean(name);


        //如果当前单例池中存在, 则直接返回
        if(singletons.containsKey(name)){
            return singletons.get(name);
        }

        //不存在则创建
        return createBean(name);
    }

    /**
     * 获取bean
     * @param type
     * @return
     * @throws Exception
     */
    @Override
    public <T> T getBean(Class<T> type) throws Exception{
        //根据类型获取name的数组
        String[] names = getBeanNamesForType(type);
        //存在多个则报错
        if(names.length==0) throw new RuntimeException("类型"+type.getName()+"容器没有管理实例");
        if(names.length>=2) throw new RuntimeException("类型"+type.getName()+"管理的实现存在多个，不知道要返回哪一个");
        //用name去获取bean
        return (T)getBean(names[0]);
    }



    /**
     * 检查是否容器管理了这个类型的bean
     * @param name
     */
    private void checkIsManagedBean(String name){
        if(!beanDefinitionNames.contains(name)){
            throw new RuntimeException("无法返回容器未管理的接口");
        }
    }

    /**
     * 通过类搜索当前容器负责管理的bean的name数组
     * @param type
     * @return
     */
    public String[] getBeanNamesForType(Class<?> type){
        //如果曾经获取过这个类型对应的names，则直接从缓存中返回
        if(singletonBeanNamesByType.containsKey(type)){
            return singletonBeanNamesByType.get(type);
        }

        //如果不存在，则循环所有bean定义获取names数组
        return doGetBeanNamesForType(type);
    }

    /**
     * 通过类搜索当前容器负责管理的bean的name数组（真正干活的）
     * @param type
     * @return
     */
    public String[] doGetBeanNamesForType(Class<?> type){
        List<String> result = new ArrayList<>();
        //循环所有name，查看定义的type是否为本次搜寻的类型 或者他的子类 或者他的实现
        for (String name : beanDefinitionNames) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(name);
            Class definitionType = beanDefinition.getType();
            //类型相同或者为子类实现则放入返回列表
            if(type.equals(definitionType) || type.isAssignableFrom(definitionType)){
                result.add(name);
                continue;
            }
        }
        //加入缓存
        singletonBeanNamesByType.put(type,StringUtils.toStringArray(result));
        //返回
        return singletonBeanNamesByType.get(type);
    }


    /**
     * 创建bean
     * @param name
     * @return
     * @throws Exception
     */
    private Object createBean(String name) throws Exception{
        //获取bean定义
        BeanDefinition beanDefinition = beanDefinitionMap.get(name);
        //如果是工厂bean则调用创建工厂bean的方法，否则调用创建普通bean的方法
        if(beanDefinition.isFactoryBean()){
            return doCreateFactoryBean(beanDefinition);
        }else{
            return doCreateBean(beanDefinition);
        }
    }

    /**
     * 真正去创建普通bean
     * @param beanDefinition
     * @return
     * @throws Exception
     */
    private Object doCreateBean(BeanDefinition beanDefinition) throws Exception{
        String name = beanDefinition.getName();
        Class type = beanDefinition.getType();
        //实例化
        Object bean = type.newInstance();
        //先把未注入其他属性的bean放入容器
        singletons.put(name,bean);
        //查看所有属性 判断是否需要自动注入
        Field[] fields = type.getDeclaredFields();
        for (Field field : fields) {
            Autowire annotation = field.getAnnotation(Autowire.class);
            //如果存在自动注入的注解，则注入
            if(annotation!=null){
                //赋值: 先getBean让容器管理这个实现，然后放入变量
                field.setAccessible(true);
                //注入：如果指定了name，则直接注入指定name的bean，否则根据类型获取bean
                String fieldNeanName = annotation.name();
                if(StringUtils.isNotBlank(fieldNeanName)){
                    field.set(bean,getBean(fieldNeanName));
                }else{
                    field.set(bean,getBean(field.getType()));
                }
            }
        }


        return bean;
    }

    /**
     * 真正创建工厂bean
     * @param beanDefinition
     * @return
     * @throws Exception
     */
    private Object doCreateFactoryBean(BeanDefinition beanDefinition) throws Exception{
        //拿到工厂类
        Object obj = getBean(beanDefinition.getFactoryBeanName());
        if(obj==null){
            throw new RuntimeException("工厂类不存在");
        }
        //反射调用方法，获取结果
        Method method = beanDefinition.getFactoryMethod();
        Object bean = method.invoke(obj);
        //放入单例池
        singletons.put(beanDefinition.getName(),bean);

        return bean;
    }

    /**
     * 获取管理的单例池
     * @return
     */
    public Map<String,Object>  getSingletons(){
        return  singletons;
    }

}
